using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Text;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using MidletEarth.Common;
using PlaySpark.Clients;
using SubSonic;


namespace PlaySpark.Web.WebServices
{
    /// <summary>
    /// Summary description for WebService.
    /// </summary>
    public class WebService : System.Web.Services.WebService
    {
        public result result;
        public authentication authentication;
		public mappingNames mappingNames;

        private string httpRequest;
        protected int RequestLogId;

		protected Client Client;

		//protected int clientId;
		//private string clientName;

        #region Authentication
        protected bool authenticate()
        {
			bool returnValue = false;
			
			try
			{
                if (PlatformSettings.IsOnline)
				{
                    if (authentication != null && authenticateInterfaceLogin())
                    {
						

                        if (authenticateClient())
						{
							if (authenticateIP())
                            {
                                result.ResultCode = (int)PlaySpark.Components.InterfaceResult.OK;
                                //result.ResultMessage = "Successfully logged in to PlaySpark by Midlet Earth TM";
                                returnValue = true;
                            }
                        }
                    }
                }
                else
                {
                    result.ResultCode = (int)PlaySpark.Components.InterfaceResult.INTERFACE_IS_OFFLINE;
                    errorAlert(result.ResultCode, result.ResultMessage, "");
                }
			}
			catch (Exception ex)
			{
				result.ResultCode = (int)PlaySpark.Components.InterfaceResult.UNKNOWN_ERROR;
				errorAlert(result.ResultCode, result.ResultMessage, String.Format("{0}\r\n\r\n{1}", ex.Message, ex.StackTrace));
			}

            return returnValue;
        }

        protected bool authenticateInterfaceLogin()
        {
            bool returnValue = false;

            bool loginCorrect = false;
            bool passwordCorrect = false;

            if (PlatformSettings.Interface_Login == authentication.Interface_Login)
            {
                loginCorrect = true;
            }

            if (PlatformSettings.Interface_Password == authentication.Interface_Password)
            {
                passwordCorrect = true;
            }

            if ((loginCorrect) && (passwordCorrect))
            {
                returnValue = true;
            }

            if (!returnValue)
            {
                result.ResultCode = (int)PlaySpark.Components.InterfaceResult.INVALID_INTERFACE_LOGIN;
                errorAlert(result.ResultCode, result.ResultMessage, "");
            }

            return returnValue;
        }

        protected bool authenticateClient()
        {
            bool returnValue = false;

            if (authentication.ClientLogin.Trim() != string.Empty && authentication.ClientPassword.Trim() != string.Empty)
            {
				try
				{
					Query query = new Query(DAL.Client.Schema);
					query.AddWhere(DAL.Client.Columns.Login, Comparison.Equals, authentication.ClientLogin);
					query.AddWhere(DAL.Client.Columns.Password, Comparison.Equals, authentication.ClientPassword);

					//DAL.Client daoClient = new DAL.Client();
					//daoClient.LoadAndCloseReader(query.ExecuteReader());

					//if (daoClient.Id > 0)
					//{
					//    clientId = daoClient.Id;
					//    clientName = daoClient.Name;
					//}

					Client = new Client();
					Client.Load(query.ExecuteReader());
				}
				catch (Exception ex)
				{
					throw new Exception(String.Format("Could not authenticate client. {0}\r\n\r\n{1}", ex.Message, ex.StackTrace));
				}

				//if (clientId > 0)
				//{
				//    returnValue = true;
				//}

				if (Client.Id > 0)
				{
					returnValue = true;
				}
			}

			if (!returnValue)
			{
				result.ResultCode = (int)PlaySpark.Components.InterfaceResult.INVALID_CLIENT_LOGIN;
				errorAlert(result.ResultCode, result.ResultMessage, "");
			}

            return returnValue;
        }

        protected bool authenticateIP()
        {
            bool returnValue = false;

            bool accessAllowed = false;

            if (Context.Request.UserHostAddress == "127.0.0.1")
            {
                accessAllowed = true;
            }
            else
            {
               try
                {
					DAL.ClientIPCollection daoClientIps = new DAL.ClientIPCollection();
					//daoClientIps.Where(DAL.ClientIP.Columns.ClientId, clientId).OrderByAsc(DAL.ClientIP.Columns.IPAddress).Load();
					daoClientIps.Where(DAL.ClientIP.Columns.ClientId, Client.Id).OrderByAsc(DAL.ClientIP.Columns.IPAddress).Load();

					foreach (DAL.ClientIP daoClientIp in daoClientIps)
                    {
						string ipAddress = daoClientIp.IPAddress;

                        if (ipAddress == "*")
                        {
                            accessAllowed = true;
							break;
                        }
                        else if (ipAddress.EndsWith("*"))
                        {
                            if (Context.Request.UserHostAddress.StartsWith(ipAddress.Substring(0, ipAddress.IndexOf("*"))))
                            {
                                accessAllowed = true;
								break;
                            }
                        }
                        else if (ipAddress == Context.Request.UserHostAddress)
                        {
                            accessAllowed = true;
							break;
                        }
                        else
                        {
                            try
                            {
                                IPHostEntry ipE = Dns.GetHostEntry(ipAddress);
                                foreach (IPAddress ip in ipE.AddressList)
                                {
                                    if (Context.Request.UserHostAddress == ip.ToString())
                                    {
                                        accessAllowed = true;
										break;
                                    }
                                }
                            }
                            catch { }
                        }
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception(String.Format("Could not authenticate IP. {0}\r\n\r\n{1}", ex.Message, ex.StackTrace));
                }
            }

            returnValue = accessAllowed;

            if (!returnValue)
            {
                result.ResultCode = (int)PlaySpark.Components.InterfaceResult.INVALID_IP_ADDRESS;
                result.ResultMessage += String.Format(" ({0})", Context.Request.UserHostAddress);
                errorAlert(result.ResultCode, result.ResultMessage, "");
            }

            return returnValue;
        }
        #endregion

        #region Utilities

		private readonly object _lock = new object();

		private void writeTextFile(string filePath, string fileName, string text)
		{
			string path = filePath.Replace("::1", "127.0.0.1");
			string name = fileName.Replace("::1", "127.0.0.1");

			lock (_lock)
			{
				if (!Directory.Exists(path))
				{
					Directory.CreateDirectory(path);
				}

				StreamWriter writer = File.CreateText(Path.Combine(path, name));
				writer.Write(text);
				writer.Close();
			}
		}

        protected string errorAlert(int resultCode, string resultMessage, string details)
        {
            string returnValue = "";

            StringBuilder sb = new StringBuilder();
            sb.Append("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n");
            sb.Append("<Error>\r\n");
            sb.AppendFormat("\t<ResultCode>{0}</ResultCode>\r\n", resultCode);
            sb.AppendFormat("\t<ResultMessage>{0}</ResultMessage>\r\n", resultMessage);

            sb.Append("\t<HttpRequest>\r\n");
            sb.Append("\t\t<![CDATA[\r\n");
            sb.AppendFormat("{0}\r\n", httpRequest);
            sb.Append("\t\t]]>\r\n");
            sb.Append("\t</HttpRequest>\r\n");

            if (details.Trim() != string.Empty)
            {
                sb.Append("\t<Details>\r\n");
                sb.Append("\t\t<![CDATA[\r\n");
                sb.AppendFormat("{0}\r\n", details);
                sb.Append("\t\t]]>\r\n");
                sb.Append("\t</Details>\r\n");
            }

            sb.Append("</Error>\r\n");

            string alertXml = sb.ToString();

            string logFilePath = String.Format(@"{0}{1}PlaySpark\{2:yyyy.MM}\{2:dd_dddd}\{3}", PlatformSettings.ApplicationRoot, PlatformSettings.LogPath, DateTime.Now, Context.Request.UserHostAddress);
            string logFileName = String.Format("{0:yyyy.MM.dd_HH.mm.ss}.{1}_ERROR.txt", DateTime.Now, DateTime.Now.Millisecond);

			writeTextFile(logFilePath, logFileName, alertXml);

            string smtpServer = PlatformSettings.SMTPServer;
            string[] toAddresses = new string[] { "alert@airgames.co.za" };
            string fromAddress = "server@airgames.co.za";
            string subject = "PlaySpark error at " + DateTime.Now.ToString();
            string body = alertXml;

            Alerts.SendEmail(smtpServer, toAddresses, fromAddress, subject, body);

            returnValue = alertXml;
            return returnValue;
        }

        /// <summary>
        /// Returns CountryId based on IP Address
        /// </summary>
        /// <returns></returns>
        private int getGeoCountryId()
        {
            int returnValue = 0;

            MidletEarth.Geography.GeoTarget gt = new MidletEarth.Geography.GeoTarget(Server.MapPath("data/GeoIP.dat"));
            MidletEarth.Geography.Country country = gt.GetCountry(Context.Request.UserHostAddress);

			DAL.Country daoCountry = new DAL.Country();
			daoCountry.LoadAndCloseReader(DAL.Country.FetchByParameter(DAL.Country.Columns.Code, country.Code));

			if (daoCountry.Id > 0)
			{
				returnValue = daoCountry.Id;
			}

            return returnValue;
        }
        #endregion

        #region Log Requests
        /// <summary>
        /// Logs current Request
        /// </summary>
        protected void logHttpRequest(string origin)
        {
            if (httpRequest.Length == 0)
            {
                try
                {
                    httpRequest = getInputStream();

					DAL.RequestLog daoRequestLog = new DAL.RequestLog();
					daoRequestLog.Url = Context.Request.RawUrl;
                    daoRequestLog.Request = TextEncoding.Base64EncodeToString(httpRequest);
                    daoRequestLog.Ip = Context.Request.UserHostAddress;
                    daoRequestLog.CountryId = getGeoCountryId();
                    daoRequestLog.RequestLogTypeId = 1;
					daoRequestLog.Save(ProviderNames.PlaySpark);

                    try
                    {
                        RequestLogId = daoRequestLog.Id;
                    }
                    catch { }

                    string logFilePath = String.Format(@"{0}{1}PlaySpark\{2:yyyy.MM}\{2:dd_dddd}\{3}", PlatformSettings.ApplicationRoot, PlatformSettings.LogPath, DateTime.Now, Context.Request.UserHostAddress);
                    string logFileName = String.Format("{0:yyyy.MM.dd_HH.mm.ss}.{1}_{2}.txt", DateTime.Now, DateTime.Now.Millisecond, origin);

					writeTextFile(logFilePath, logFileName, httpRequest);
                }
                catch (Exception ex)
                {
                    errorAlert(-1, "Could not log request", String.Format("{0}\r\n\r\n{1}", ex.Message, ex.StackTrace));
                }
            }
        }

        /// <summary>
        /// Returns the contents of the HTTP InputStream
        /// </summary>
        /// <returns></returns>
        private string getInputStream()
        {
            string returnValue = "";

            Stream inputStream = Context.Request.InputStream;
            byte[] chars = new byte[256];
            StringBuilder sb = new StringBuilder();
            int byteCount;

            inputStream.Position = 0;
            do
            {
                byteCount = inputStream.Read(chars, 0, 256);
                sb.Append(Encoding.ASCII.GetString(chars, 0, byteCount));
            } while (byteCount > 0);

            inputStream.Close();

            returnValue = sb.ToString();

            return returnValue;
        }
        #endregion

        public WebService()
        {
            result = new result();
            result.Activate();

            //clientId = -1;

            httpRequest = "";
            RequestLogId = -1;
        }
    }
}
